package com.jiao.table.csql;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.jiao.comm.exception.ResultMappingException;
import com.jiao.table.annotation.ColunmMapping;
import com.jiao.table.config.CacheDataLoad;
import lombok.SneakyThrows;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 缓存结果映射.
 * @Author Vincent.jiao
 * @Date 2022/5/20 9:15
 */
public class CacheResultMapping {
    private static Map<Class, Map<String, Field>> classFieldMap = new ConcurrentHashMap<>();

    public static  <T> T toBean(String tableName, String json) {
        Class clazz =  CacheDataLoad.getTableEntityClass(tableName);
        return toBean(tableName, json, clazz, clazz);
    }

    public static  <T> T toBean(String tableName, String json, Class toClass) {
        Class clazz =  CacheDataLoad.getTableEntityClass(tableName);
        return toBean(tableName, json, clazz, toClass);
    }

    public static <T> T toBean(String tableName, String json, Class thisClass, Class toClass) {
        if(json == null || json.isEmpty()) {
            return null;
        }

        T result = (T) JSONUtil.toBean(json, thisClass);
        return thisClass.equals(toClass) ?
                result : toBean(result, toClass);
    }

    public static <T> T toBean(Object sourceVal, Class toClass) {
        Map<String, Field> sourceAllFieldMap = getFieldMap(sourceVal.getClass());
        T result = null;
        try {
            Field[] resultAllField = ClassUtil.getDeclaredFields(toClass);
            result = (T) toClass.newInstance();
            for (Field item : resultAllField) {
                ColunmMapping colunmMapping = item.getAnnotation(ColunmMapping.class);
                String mapperName = colunmMapping == null ? item.getName() :  colunmMapping.name();
                Field field = sourceAllFieldMap.get(mapperName);
                if(field != null) {
                    field.setAccessible(true);
                    item.setAccessible(true);
                    item.set(result, field.get(sourceVal));
                }
            }
        } catch (IllegalAccessException | InstantiationException e) {
            throw new ResultMappingException(e.getMessage(), e);
        }

        return result;
    }

    public static Map<String, Field> getFieldMap(Class clazz) {
        Map<String, Field> sourceAllFieldMap = classFieldMap.get(clazz);
        if(sourceAllFieldMap == null) {
            Field[] allField = ClassUtil.getDeclaredFields(clazz);
            sourceAllFieldMap = Arrays.asList(allField).stream().collect(Collectors.toMap(Field::getName, field -> field));
        }

        return sourceAllFieldMap;
    }
}
